package com.log4jviewer.ui.preferences.filters;

import java.io.IOException;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;

import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerEditor;
import org.eclipse.jface.viewers.TableViewerFocusCellManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.log4jviewer.filters.ConfigFilterSaver;
import com.log4jviewer.filters.FilterItemModel;
import com.log4jviewer.filters.FilterModel;
import com.log4jviewer.ui.preferences.DefaultPreferences;

/**
 * Controller for filters' preferences. It's a channel between filter's and filter items' ui components.
 * 
 * @authors <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko </a></br><a
 *          href="mailto:Daniil.Yaroslavtsev@gmail.com">Daniil Yaroslavtsev</a>
 */
public class FilterController {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private FilterRuleSettings filterRuleSettings;

    public void setFilterRuleSettings(final FilterRuleSettings filterRuleSettings) {
        this.filterRuleSettings = filterRuleSettings;
    }

    public void saveFilters(final String filtersConfigFilePath, final List<FilterModel> filterModels)
            throws IOException,
            ParserConfigurationException {
        ConfigFilterSaver filterSaver = new ConfigFilterSaver();
        filterSaver.saveFilters(filtersConfigFilePath, filterModels);
        logger.debug("Filters were saved.");
    }

    public void saveActiveFilter(final IPreferenceStore store, final int activeFilterIndex) {
        store.setValue(DefaultPreferences.FilterPreferences.getId(), activeFilterIndex);
        logger.debug("Active filter with index = {} was saved.", activeFilterIndex);
    }

    public void removeItemsFromFilterRuleTable() {
        filterRuleSettings.getTableViewer().getTable().removeAll();
        filterRuleSettings.checkAndSetAddButtonState();
    }

    public void addFilterTableListener(final TableViewer filterTableViewer,
            final FilterContentProvider filterContentProvider) {
        filterTableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                TableViewer filterRuleTableViewer = filterRuleSettings.getTableViewer();
                filterRuleTableViewer.getTable().removeAll();
                int selectedFilterIndex = filterTableViewer.getTable().getSelectionIndex();

                if (selectedFilterIndex >= 0) {
                    filterContentProvider.setSelectedFilterIndex(selectedFilterIndex);
                    List<FilterItemModel> filterItemModels = filterContentProvider
                            .getFilterItems(selectedFilterIndex);
                    filterRuleTableViewer.setInput(filterItemModels);
                }
                filterRuleSettings.checkAndSetAddButtonState();
                logger.debug("Index of selected filter = {}", selectedFilterIndex);
            }
        });
    }

    public void setCellEditingStrategy(final TableViewer tableViewer) {

        TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(tableViewer,
                new FocusCellOwnerDrawHighlighter(tableViewer));

        ColumnViewerEditorActivationStrategy activationStrategy =
                new ColumnViewerEditorActivationStrategy(tableViewer) {
                    @Override
                    protected boolean isEditorActivationEvent(
                            final ColumnViewerEditorActivationEvent event) {
                        boolean result = false;

                        Table table = tableViewer.getTable();
                        int selectedCellIndex = getSelectedCellIndex(event, table);

                        if (table.getColumnCount() == FilterSettings.COLUMN_COUNT) {
                            result = getFilterTableActivationStrategy(event, selectedCellIndex);
                        } else if (table.getColumnCount() == FilterRuleSettings.COLUMN_NUMBER) {
                            result = getFilterRuleTableActivationStrategy(event, selectedCellIndex);
                        }

                        return result;
                    }

                };
        TableViewerEditor.create(tableViewer, focusCellManager, activationStrategy,
                ColumnViewerEditor.TABBING_HORIZONTAL
                        | ColumnViewerEditor.TABBING_MOVE_TO_ROW_NEIGHBOR
                        | ColumnViewerEditor.TABBING_VERTICAL
                        | ColumnViewerEditor.KEYBOARD_ACTIVATION);
    }

    private boolean getFilterTableActivationStrategy(final ColumnViewerEditorActivationEvent evt,
            final int selectedCellIndex) {
        boolean result = false;

        if (selectedCellIndex == FilterSettings.ENABLE_COLUMN_INDEX) {
            result = (evt.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION);
        } else {
            result = (evt.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL)
                    || (evt.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION)
                    || ((evt.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED) && (evt.keyCode == SWT.CR))
                    || ((evt.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED) && (evt.keyCode == SWT.F2))
                    || ((evt.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED)
                    && (evt.keyCode == SWT.KEYPAD_CR))
                    || ((evt.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED)
                            && (evt.keyCode > 31) && (evt.keyCode < 127))
                    || ((evt.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED)
                            && (evt.keyCode >= SWT.KEYPAD_MULTIPLY) && (evt.keyCode <= SWT.KEYPAD_9))
                    || (evt.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC);
        }
        return result;
    }

    private boolean getFilterRuleTableActivationStrategy(final ColumnViewerEditorActivationEvent evt,
            final int selectedCellIndex) {
        boolean result = false;

        if (selectedCellIndex == FilterRuleSettings.ENABLE_COLUMN_INDEX) {
            result = (evt.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION);
        } else {
            result = (evt.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL)
                    || ((evt.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED) && (evt.keyCode == SWT.F2))
                    || (evt.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION)
                    || (evt.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC);
        }
        return result;
    }

    // Looking for selected cell index in table item
    private int getSelectedCellIndex(final ColumnViewerEditorActivationEvent event,
            final Table table) {
        int cellIndex = -1;

        if (event.sourceEvent instanceof MouseEvent) {
            int x = ((MouseEvent) event.sourceEvent).x;
            int y = ((MouseEvent) event.sourceEvent).y;
            Point clickLocation = new Point(x, y);
            TableItem tableItem = table.getItem(clickLocation);

            if (tableItem != null) {
                for (int i = 0; i < table.getColumnCount(); i++) {
                    Rectangle rect = tableItem.getBounds(i);

                    if (rect.contains(clickLocation)) {
                        cellIndex = i;
                        break;
                    }
                }
            }
        }
        return cellIndex;
    }
}
